﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Log;
using UtilZ.Dotnet.Ex.DataStruct;

namespace ConAppCore
{
    internal class TestAsynQueue
    {
        private static TestAsynQueue _obj;
        public static void Test()
        {
            _obj = new TestAsynQueue();
            _obj.Start();


        }

        public static void Stop()
        {
            if (_obj != null)
            {
                _obj.Flag = false;
            }
        }
















        //private AsynQueueX<long> _asynQueue;
        //private AsynQueueZ<long> _asynQueue;
        private AsynQueue<long> _asynQueue;
        //private AsynQueueCore<long> _asynQueue;

        private TestAsynQueue()
        {
            //this._asynQueue = new AsynQueue<long>(this.QueueCallback, new AsynQueueOptions() { ThreadName = "异步队列线程" });
            var options = new AsynQueueOptions()
            {
                BatchCount = 10000,
                BatchMillisecondsTimeout = 1000,
                Capcity = 1000,
                FullMode = ContainerFullMode.DropNew
            };

            //this._asynQueue = new AsynQueueX<long>(this.QueueCallback2, options);
            // this._asynQueue = new AsynQueueZ<long>(this.QueueCallback2, options);
            this._asynQueue = new AsynQueue<long>(this.QueueCallback2, options);
            //this._asynQueue = new AsynQueueCore<long>(this.QueueCallback2, options);
        }


        private void QueueCallback2(long[] values, int count, object tag)
        {
            Loger.Debug($"{TimeEx.GetTimestamp()} count:{count}");
        }

        private void QueueCallback(long value)
        {
            Loger.Debug($"value:{value}");
        }

        internal void Start()
        {
            //SpinLockSample1();

            this._asynQueue.Start();

            var produceInterval = 100;
            var addTimeout = 100;
            var state = new Tuple<int, int>(produceInterval, addTimeout);

            for (int i = 0; i < Environment.ProcessorCount - 1; i++)
            {
                Task.Factory.StartNew(this.ProduceThreadMethod, state);
            }
        }

        /// <summary>
        /// 原子锁
        /// </summary>
        //private readonly SpinLock _spinLock = new SpinLock();
        void SpinLockSample1()
        {
            //SpinLock sl = new SpinLock();
            SpinLock _spinLock = new SpinLock();

            StringBuilder sb = new StringBuilder();

            // Action taken by each parallel job.
            // Append to the StringBuilder 10000 times, protecting
            // access to sb with a SpinLock.
            Action action = () =>
            {
                bool gotLock = false;
                for (int i = 0; i < 10000; i++)
                {
                    gotLock = false;
                    try
                    {
                        //sl.Enter(ref gotLock);
                        _spinLock.Enter(ref gotLock);
                        sb.Append((i % 10).ToString());
                    }
                    finally
                    {
                        // Only give up the lock if you actually acquired it
                        if (gotLock)
                            _spinLock.Exit();
                    }
                }
            };

            // Invoke 3 concurrent instances of the action above
            Task.Factory.StartNew(action);
            //Parallel.Invoke(action, action, action);

            // Check/Show the results
            Console.WriteLine("sb.Length = {0} (should be 30000)", sb.Length);
            Console.WriteLine("number of occurrences of '5' in sb: {0} (should be 3000)",
                sb.ToString().Where(c => (c == '5')).Count());
        }


        private bool _flag = true;
        public bool Flag
        {
            get { return _flag; }
            set { _flag = value; }
        }

        private long _value = 0;
        private long _max = 10000000;
        private void ProduceThreadMethod(object obj)
        {
            var tp = (Tuple<int, int>)obj;
            int produceInterval2 = tp.Item1;
            int addTimeout2 = tp.Item2;
            //while (_value< _max)
            while (_flag)
            {
                try
                {
                    this._asynQueue.Enqueue(Interlocked.Increment(ref this._value));
                }
                catch (TimeoutException tex)
                {
                    Loger.Warn(tex);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }

                if (produceInterval2 > 0)
                {
                    Thread.SpinWait(produceInterval2);
                    //Thread.Sleep(produceInterval2);
                }
            }
        }

    }
}
